<?php
/*******************************************************************************
 * LightPress - http://lightpress.org
 *
 * Copyright 2004-2005 Ludovico Magnocavallo.
 * Released under the GPL http://www.gnu.org/copyleft/gpl.html
 *
 * developers:
 *              Ludovico Magnocavallo <ludo@asiatica.org>
 *              Jerome Lavigne <jerome@vapourtrails.ca>
 *              Luca Lizzeri <luca.lizzeri@gmail.com>
 *
 * $Id: Template.php 342 2006-05-16 09:36:29Z ludo $
 ******************************************************************************/

class Template {
    // general properties
    var $classname	= 'Template';
    var $root       = '';
    var $unresolved = 'keep';
    var $deep_parsing = false;
    // utility arrays
    var $_files		= array();	// holds file handles as keys, filenames as values
    var $_templates	= array();	// holds raw templates from files and blocks
    var $_vars		= array();	// holds variable names as keys, values as values

    function Template($root = '', $unresolved = 'keep', $deep_parsing = true) {
        // unresolved variables
        $this->unresolved = $unresolved; //$unresolved;	// keep|remove|comment
        $this->deep_parsing = $deep_parsing;
        // root directory
        if (!empty($root))
            $this->setRoot($root);
    }

    function setRoot($root) {
        if (empty($root) || !is_dir($root))
            return $this->raiseError("root $root not found or not a directory", 'setRoot', __LINE__, E_USER_ERROR);
        if (substr($root, - 1) != '/') $root .= '/';
        $this->root = $root; 
    }
        
    function get($name = null) {
        if (!(substr($name, 0, 1) == '_'))
            return $this->$name;
    }
        
    function getVars() {
        return($this->_vars);
    }
        
    function getVar($var) {
        return $this->_vars[$var];
    }
        
    function getFile($file) {
        return $this->_files[$file];
    }
        
    function getTemplate($template) {
        return $this->_templates[$template];
    }
        
    function setFile($handle, $filename = '') {
        if (is_array($handle))
            $this->_files = array_merge($this->_files, $handle);
        else if (is_string($handle))
            $this->_files[$handle] = $filename;
        else
            return $this->raiseError("handle $handle is of unsupported type", 'setFile', __LINE__);
    }

    function setVar($name, $value = '') {
        if (is_array($name))
            $this->_vars = array_merge($this->_vars, $name);
        else if (is_string($name))
            $this->_vars[$name] = $value;
        else if (is_int($name))
            $this->_vars[$name] = $value . '';
        else
            return $this->raiseError("name $name is of unsupported type", 'setVar', __LINE__);
    }

    function loadFile($handle, $raise_error=true) {
        if (!isset($this->_files[$handle]))
            return $raise_error ? $this->raiseError("handle $handle is empty", 'loadFile', __LINE__) : null;
        if  (!file_exists($this->root . $this->_files[$handle])) {
            return $raise_error ? $this->raiseError(
                "file " . $this->root . $this->_files[$handle] . " for handle $handle is non existent",
                'loadFile',
                __LINE__,
                E_USER_ERROR) : null;
        }
        return(
            join('', file($this->root . $this->_files[$handle]))
			);
    }

    function setBlock($parent, $handle, $replacement = '', $raise_error=true) {
        if (empty($this->_templates[$parent])) {
            $loaded_file = $this->loadFile($parent);
            if (is_null($loaded_file))
                return $loaded_file;
            $this->_templates[$parent] = $loaded_file;
        }
        if (empty($replacement)) $replacement = strtoupper($handle);
        $re = '/<!--\\s+BEGIN\\s+' . $handle . '\\s+-->(.*?)<!--\\s+END\\s+' . $handle . '\\s+-->/s';
        if (!preg_match($re, $this->_templates[$parent], $m)) {
            return $raise_error ? $this->raiseError("block $handle not found", 'setBlock', __LINE__, E_USER_ERROR) : false;
        }
        $this->_templates[$parent] = str_replace($m[0], '{' . $replacement . '}', $this->_templates[$parent]);
        $this->_templates[$handle] = $m[1];
        return true;
    }

    function parseBlock($target, $handle, &$vars, $parent = '') {
        if (!empty($parent)) {
            $ret = $this->setBlock($parent, $handle, $target);
            if (is_null($ret)) return $ret;
        } else if (empty($this->_templates[$handle])) {
            return $this->raiseError("no block template for handle $handle", 'parseBlock', __LINE__, E_USER_ERROR);
        }
        $block = $this->_templates[$handle];
        $template = '';
        $varnames = array();
        $string = true;
        foreach ($vars as $k => $v) {
            if (is_array($vars[$k])) {
                $string = false;
                foreach ($vars[$k] as $key => $value) {
                    $varnames[] = '{' . $handle . '_' . $key . '}';
                }
            } else if (is_string($vars[$k]) || is_int($vars[$k])) {
                $varnames[] = '{' . $handle . '_0}';
            } else {
                return $this->raiseError("unknown vars value", 'parseBlock', __LINE__, E_USER_NOTICE);
            }
            break;
        }
        $varnames[] = '{' . $handle . '_}';
        foreach ($vars as $k => $v) {
            if ($string) {
                $template .= str_replace($varnames, array($vars[$k], $k), $block);
            } else {
                array_push($vars[$k], $k);
                $template .= str_replace($varnames, $vars[$k], $block);
            }
        }
        $this->_vars[$target] = $template;
    }
        
    function &parse($target, $handle, $append=false, $raise_error=true) {
        if (empty($this->_templates[$handle])) {
            $loaded_file = $this->loadFile($handle, $raise_error);
            if (is_null($loaded_file))
                return $loaded_file;
            $this->_templates[$handle] = $loaded_file;
        }
        $template = preg_replace_callback('/{([A-Za-z0-9_\-]+)}/', array($this, 'subst'), $this->_templates[$handle]);
        if ($append && isset($this->_vars[$target]))
            $this->_vars[$target] .= $template;
        else
            $this->_vars[$target] = $template;
        return $template;
    }

    function subst($handle) {
        
        $handle =& $handle[1];
        
        $vars =& $this->_vars;
        
        if (isset($vars[$handle])) {
            if ($this->deep_parsing)
                return preg_replace_callback('/{([A-Za-z0-9_\-]+)}/', array($this, 'subst'), $vars[$handle]);
            else
                return $vars[$handle];
        }
        switch ($this->unresolved) {
            case 'comment':
                return('<!-- {' . $handle . '} -->');
            case 'remove':
                return('');
            default:    // 'keep', catches non standard values
                return('{' . $handle . '}');
        }
    }

    function pparse($target, $handle, $append = false) {
        $ret = $this->parse($target, $handle, $append);
        echo($ret);
    }
        
    function raiseError($message, $method, $line, $type=E_USER_WARNING) {
        trigger_error(
            sprintf("%s.%s() line %d: %s", get_class($this), $method, $line, $message),
            $type);
    }

}

?>